home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Contributed / SpriteWorld / SpriteWorld Files / Utils / SWParticles.c < prev    next >
Encoding:
Text File  |  2000-10-06  |  16.2 KB  |  508 lines  |  [TEXT/CWIE]

  1. ///--------------------------------------------------------------------------------------
  2. // SWParticles.c - by Matthew Foodim, with changes by Anders Björklund and Vern Jensen.
  3. //
  4. // Note: NewParticle currently does not add any new particles if there is no room in the
  5. // gParticleArray.
  6. ///--------------------------------------------------------------------------------------
  7.  
  8. #include <SWIncludes.h>
  9. #include <SWParticles.h>
  10.  
  11. ///--------------------------------------------------------------------------------------
  12. //    Global variables, private to this file
  13. ///--------------------------------------------------------------------------------------
  14.  
  15. static ParticlePtr        gParticleArray = NULL;        // An array containing data for each particle.
  16. static long                gLowestUnusedElement;        // Start at this element when inserting a new particle
  17. static long                gLastElementInUse;            // The highest element that has an active particle.
  18. static long                gNumberOfParticlesActive;    // Number of array elements currently in use
  19. static SWFixed            gGravity;                    // downward acceleration
  20. static unsigned long    gNumArrayElements;            // Size of array, unsigned to avoid the 68K optimizer bug
  21. static Boolean            gParticlesInitialized = false;    // Was InitParticles called successfully?
  22.  
  23. ///--------------------------------------------------------------------------------------
  24. //    Unroll code for the different depths, to avoid any per-pixel depth checks
  25. //    Macros, to avoid the bug-prone huge amounts of copy and paste.
  26. //    The optimizer will make it the same, anyway.
  27. //        "Better living through pre-processor"                         --afb
  28. ///--------------------------------------------------------------------------------------
  29.  
  30. #define FOREACH_DEPTH_DO(depth,instructions)    \
  31.     switch ( depth )    \
  32.     {    case  8:    { int DEPTH=8;  instructions } break;    \
  33.         case 16:    { int DEPTH=16; instructions } break;    \
  34.         case 32:    { int DEPTH=32; instructions } break; }
  35.  
  36. #define GET_DEPTH_PIXEL(frame,x,y)                \
  37.     ( DEPTH==8 ?    *SW_PIXELPTR8(frame,x,y) :                \
  38.     ( DEPTH==16 ?    *SW_PIXELPTR16(frame,x,y) :                \
  39.                     *SW_PIXELPTR32(frame,x,y) ))
  40.  
  41. #define SET_DEPTH_PIXEL(frame,x,y,c)            \
  42.       if(DEPTH==8)    *SW_PIXELPTR8(frame,x,y)=c;  else        \
  43.     { if(DEPTH==16)    *SW_PIXELPTR16(frame,x,y)=c; else        \
  44.                     *SW_PIXELPTR32(frame,x,y)=c; }
  45.  
  46. ///--------------------------------------------------------------------------------------
  47. //  InitParticles
  48. ///--------------------------------------------------------------------------------------
  49.  
  50. SW_FUNC OSErr InitParticles(long maxNumParticles, SWFixed gravity)
  51. {
  52.     OSErr    err = noErr;
  53.     
  54.     gNumArrayElements = maxNumParticles;
  55.     gGravity = gravity;
  56.     
  57.         // Allocate memory for the particle array
  58.     gParticleArray = (ParticlePtr) NewPtr(sizeof(Particle) * gNumArrayElements);
  59.     
  60.     if (gParticleArray == NULL)
  61.     {
  62.         err = memFullErr;
  63.     }
  64.     else
  65.     {
  66.         ClearParticles();
  67.     }
  68.     
  69.     gLowestUnusedElement = 0;
  70.     gLastElementInUse = 0;
  71.     gNumberOfParticlesActive = 0;
  72.     
  73.     if (err == noErr)
  74.         gParticlesInitialized = true;
  75.     else
  76.         gParticlesInitialized = false;
  77.     
  78.     return    err;
  79. }
  80.  
  81.  
  82. ///--------------------------------------------------------------------------------------
  83. //  ClearParticles
  84. ///--------------------------------------------------------------------------------------
  85.  
  86. SW_FUNC void ClearParticles( void )
  87. {    
  88.     long    curParticle;
  89.     
  90.     SW_ASSERT(gParticleArray != NULL);
  91.     
  92.     for (curParticle = 0; curParticle < gNumArrayElements; curParticle++)
  93.     {
  94.         gParticleArray[curParticle].lifeRemaining = 0;
  95.     }
  96.     
  97.     gLowestUnusedElement = 0;
  98.     gLastElementInUse = 0;
  99.     gNumberOfParticlesActive = 0;
  100. }
  101.  
  102.  
  103. ///--------------------------------------------------------------------------------------
  104. //    NewParticle - Create a new particle
  105. ///--------------------------------------------------------------------------------------
  106.  
  107. SW_FUNC void NewParticle(
  108.     unsigned long color,
  109.     SWFixed    horizLoc,
  110.     SWFixed    vertLoc,
  111.     SWFixed    horizSpeed,
  112.     SWFixed    vertSpeed,
  113.     short lifeRemaining)
  114. {
  115.     if (gParticlesInitialized == false)
  116.         return;
  117.     
  118.         // Add a particle only if there is room.
  119.     if (gNumberOfParticlesActive < gNumArrayElements)
  120.     {
  121.             // Find the lowest unused array element
  122.             // (Note: this assumes there *is* an available element.)
  123.         while (gParticleArray[gLowestUnusedElement].lifeRemaining > 0)
  124.             gLowestUnusedElement++;
  125.         
  126.         SW_ASSERT(gLowestUnusedElement < gNumArrayElements);
  127.         
  128.         gParticleArray[gLowestUnusedElement].lifeRemaining = lifeRemaining;
  129.         gParticleArray[gLowestUnusedElement].color = color;
  130.         gParticleArray[gLowestUnusedElement].horizSpeed = horizSpeed;
  131.         gParticleArray[gLowestUnusedElement].vertSpeed = vertSpeed;
  132.         gParticleArray[gLowestUnusedElement].horizLoc = horizLoc;
  133.         gParticleArray[gLowestUnusedElement].vertLoc = vertLoc;
  134.         gParticleArray[gLowestUnusedElement].oldHorizLoc = horizLoc;
  135.         gParticleArray[gLowestUnusedElement].oldVertLoc = vertLoc;
  136.     
  137.             // If the new particle is higher than gLastElementInUse, extend it.
  138.         if (gLowestUnusedElement > gLastElementInUse)
  139.             gLastElementInUse = gLowestUnusedElement;
  140.     
  141.         gNumberOfParticlesActive++;
  142.         gLowestUnusedElement++;
  143.     }
  144. }
  145.  
  146. #pragma mark -
  147.  
  148. ///--------------------------------------------------------------------------------------
  149. //  EraseParticlesOffscreen
  150. ///--------------------------------------------------------------------------------------
  151.  
  152. SW_FUNC void    EraseParticlesOffscreen( SpriteWorldPtr spriteWorldP )
  153. {
  154.     Particle        *particlePtr;
  155.     FramePtr        workFrameP = spriteWorldP->workFrameP,
  156.                     backFrameP = spriteWorldP->backFrameP;
  157.     int                visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
  158.     int                horizLoc, vertLoc;
  159.     long            index, count = gLastElementInUse;
  160.     
  161.     SW_ASSERT(spriteWorldP != NULL);
  162.     SW_ASSERT(workFrameP != NULL && workFrameP->isFrameLocked);
  163.     SW_ASSERT(workFrameP->frameDepth >= 8);
  164.     SW_ASSERT(gParticlesInitialized == true);
  165.     
  166.     visBoundsLeft = workFrameP->frameRect.left;
  167.     visBoundsRight = workFrameP->frameRect.right;
  168.     visBoundsTop = workFrameP->frameRect.top;
  169.     visBoundsBottom = workFrameP->frameRect.bottom;
  170.     
  171.     START_32_BIT_MODE
  172.     
  173.     FOREACH_DEPTH_DO
  174.     (
  175.         spriteWorldP->pixelDepth,
  176.         for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
  177.         {
  178.             if (particlePtr->lifeRemaining>0)
  179.             {
  180.                 horizLoc = SW_FIX2INT(particlePtr->oldHorizLoc);
  181.                 vertLoc = SW_FIX2INT(particlePtr->oldVertLoc);
  182.  
  183.                 if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
  184.                     (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
  185.                 {
  186.                     SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ,
  187.                         GET_DEPTH_PIXEL( backFrameP, horizLoc, vertLoc ) )
  188.                 }
  189.             }
  190.         }
  191.     )
  192.     
  193.     END_32_BIT_MODE
  194. }
  195.  
  196.  
  197. ///--------------------------------------------------------------------------------------
  198. //  EraseParticlesScrollingOffscreen
  199. ///--------------------------------------------------------------------------------------
  200.  
  201. SW_FUNC void    EraseParticlesScrollingOffscreen( SpriteWorldPtr spriteWorldP )
  202. {
  203.     Particle    *particlePtr;
  204.     FramePtr    workFrameP = spriteWorldP->workFrameP,
  205.                 backFrameP = spriteWorldP->backFrameP;
  206.     int            visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
  207.     int            horizLoc, vertLoc;
  208.     long        index, count = gLastElementInUse;
  209.  
  210.     SW_ASSERT(spriteWorldP != NULL);
  211.     SW_ASSERT(spriteWorldP->workFrameP != NULL && workFrameP->isFrameLocked);
  212.     SW_ASSERT(workFrameP->frameDepth >= 8);
  213.     SW_ASSERT(gParticlesInitialized == true);
  214.     
  215.     visBoundsLeft = spriteWorldP->oldVisScrollRect.left;
  216.     visBoundsRight = spriteWorldP->oldVisScrollRect.right;
  217.     visBoundsTop = spriteWorldP->oldVisScrollRect.top;
  218.     visBoundsBottom = spriteWorldP->oldVisScrollRect.bottom;
  219.     
  220.     START_32_BIT_MODE
  221.     
  222.     FOREACH_DEPTH_DO
  223.     (
  224.         spriteWorldP->pixelDepth,
  225.         for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
  226.         {
  227.             if (particlePtr->lifeRemaining>0)
  228.             {
  229.                 horizLoc = SW_FIX2INT(particlePtr->oldHorizLoc);
  230.                 vertLoc = SW_FIX2INT(particlePtr->oldVertLoc);
  231.  
  232.                 if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
  233.                     (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
  234.                 {
  235.                         // Make the particle's points local to the offscreen area
  236.                         // we have to offset to the *old* ScrollRectOffset - which we have to calculate
  237.                     horizLoc -= spriteWorldP->backRect.right * (spriteWorldP->oldVisScrollRect.left / spriteWorldP->backRect.right);
  238.                     vertLoc -= spriteWorldP->backRect.bottom * (spriteWorldP->oldVisScrollRect.top / spriteWorldP->backRect.bottom);
  239.  
  240.                         // Wrap the pixel if it's hanging off one side of the offscreen area
  241.                     if (horizLoc >=  workFrameP->frameRect.right)
  242.                         horizLoc -= workFrameP->frameRect.right;
  243.                     else if (horizLoc < workFrameP->frameRect.left)
  244.                         horizLoc += workFrameP->frameRect.right;
  245.  
  246.                     if (vertLoc >= workFrameP->frameRect.bottom)
  247.                         vertLoc -= workFrameP->frameRect.bottom;
  248.                     else if (vertLoc < workFrameP->frameRect.top)
  249.                         vertLoc += workFrameP->frameRect.bottom;
  250.  
  251.                     SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ,
  252.                         GET_DEPTH_PIXEL( backFrameP, horizLoc, vertLoc ) )
  253.                 }
  254.             }
  255.         }
  256.     )
  257.     
  258.     END_32_BIT_MODE
  259.         
  260. }
  261.  
  262.  
  263. ///--------------------------------------------------------------------------------------
  264. //  DrawParticlesOffscreen
  265. ///--------------------------------------------------------------------------------------
  266.  
  267. SW_FUNC void    DrawParticlesOffscreen( SpriteWorldPtr spriteWorldP )
  268. {
  269.      Particle    *particlePtr;
  270.     FramePtr    workFrameP = spriteWorldP->workFrameP;
  271.      int        visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
  272.      int        horizLoc, vertLoc;
  273.      long        index, count = gLastElementInUse;
  274.  
  275.     SW_ASSERT(spriteWorldP != NULL);
  276.     SW_ASSERT(workFrameP != NULL && workFrameP->isFrameLocked);
  277.     SW_ASSERT(workFrameP->frameDepth >= 8);
  278.     SW_ASSERT(gParticlesInitialized == true);
  279.  
  280.     visBoundsLeft = workFrameP->frameRect.left;
  281.     visBoundsRight = workFrameP->frameRect.right;
  282.     visBoundsTop = workFrameP->frameRect.top;
  283.     visBoundsBottom = workFrameP->frameRect.bottom;
  284.     
  285.     START_32_BIT_MODE
  286.  
  287.     FOREACH_DEPTH_DO
  288.     (
  289.         spriteWorldP->pixelDepth,
  290.         for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
  291.         {
  292.             if (particlePtr->lifeRemaining>1)
  293.             {
  294.                 horizLoc = SW_FIX2INT(particlePtr->horizLoc);
  295.                 vertLoc = SW_FIX2INT(particlePtr->vertLoc);
  296.                 
  297.                 if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
  298.                 (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
  299.                 {
  300.                     SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc, particlePtr->color);
  301.                 }
  302.             }
  303.         }
  304.     )
  305.  
  306.     END_32_BIT_MODE
  307.  
  308. }
  309.  
  310.  
  311. ///--------------------------------------------------------------------------------------
  312. //  DrawParticlesScrollingOffscreen
  313. ///--------------------------------------------------------------------------------------
  314.  
  315. SW_FUNC void    DrawParticlesScrollingOffscreen( SpriteWorldPtr spriteWorldP )
  316. {
  317.     Particle    *particlePtr;
  318.     FramePtr    workFrameP = spriteWorldP->workFrameP;
  319.     int            visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
  320.     int            horizLoc, vertLoc;
  321.     long        index, count = gLastElementInUse;
  322.  
  323.     SW_ASSERT(spriteWorldP != NULL);
  324.     SW_ASSERT(workFrameP != NULL && workFrameP->isFrameLocked);
  325.     SW_ASSERT(workFrameP->frameDepth >= 8);
  326.     SW_ASSERT(gParticlesInitialized == true);
  327.  
  328.     visBoundsLeft = spriteWorldP->visScrollRect.left;
  329.     visBoundsRight = spriteWorldP->visScrollRect.right;
  330.     visBoundsTop = spriteWorldP->visScrollRect.top;
  331.     visBoundsBottom = spriteWorldP->visScrollRect.bottom;
  332.     
  333.     START_32_BIT_MODE
  334.         
  335.     FOREACH_DEPTH_DO
  336.     (
  337.         spriteWorldP->pixelDepth,
  338.         for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
  339.         {
  340.             if (particlePtr->lifeRemaining>1)
  341.             {
  342.                 horizLoc = SW_FIX2INT(particlePtr->horizLoc);
  343.                 vertLoc = SW_FIX2INT(particlePtr->vertLoc);
  344.  
  345.                 if ((horizLoc<visBoundsRight)&&(horizLoc>visBoundsLeft)&&
  346.                     (vertLoc<visBoundsBottom)&&(vertLoc>visBoundsTop))
  347.                 {
  348.                         // Make the particle's points local to the offscreen area
  349.                     horizLoc -= spriteWorldP->horizScrollRectOffset;
  350.                     vertLoc -= spriteWorldP->vertScrollRectOffset;
  351.  
  352.                         // Wrap the pixel if it's hanging off one side of the offscreen area
  353.                     if (horizLoc >=  workFrameP->frameRect.right)
  354.                         horizLoc -= workFrameP->frameRect.right;
  355.                     else if (horizLoc < workFrameP->frameRect.left)
  356.                         horizLoc += workFrameP->frameRect.right;
  357.  
  358.                     if (vertLoc >= workFrameP->frameRect.bottom)
  359.                         vertLoc -= workFrameP->frameRect.bottom;
  360.                     else if (vertLoc < workFrameP->frameRect.top)
  361.                         vertLoc += workFrameP->frameRect.bottom;
  362.                 
  363.                     SET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc, particlePtr->color );
  364.                 }
  365.             }
  366.         }
  367.     )
  368.     
  369.     END_32_BIT_MODE
  370.  
  371. }
  372.  
  373. #pragma mark -
  374.  
  375. ///--------------------------------------------------------------------------------------
  376. //  UpdateParticlesInWindow - moves particles, and also draws them to the window
  377. ///--------------------------------------------------------------------------------------
  378.  
  379. SW_FUNC void    UpdateParticlesInWindow( SpriteWorldPtr spriteWorldP )
  380. {
  381.      Particle    *particlePtr;
  382.      FramePtr    workFrameP = spriteWorldP->workFrameP,
  383.                 windowFrameP = spriteWorldP->windowFrameP;
  384.      int        visBoundsLeft, visBoundsRight, visBoundsTop, visBoundsBottom;
  385.      int        horizLoc, vertLoc;
  386.      long        index, count = gLastElementInUse;
  387.  
  388.     SW_ASSERT(spriteWorldP != NULL);
  389.     SW_ASSERT(windowFrameP != NULL && windowFrameP->isFrameLocked);
  390.     SW_ASSERT(windowFrameP->frameDepth >= 8);
  391.     SW_ASSERT(gParticlesInitialized == true);
  392.  
  393.     visBoundsLeft = windowFrameP->frameRect.left;
  394.     visBoundsRight = windowFrameP->frameRect.right;
  395.     visBoundsTop = windowFrameP->frameRect.top;
  396.     visBoundsBottom = windowFrameP->frameRect.bottom;
  397.  
  398.     START_32_BIT_MODE
  399.  
  400.     FOREACH_DEPTH_DO
  401.     (
  402.         spriteWorldP->pixelDepth,
  403.         for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
  404.         {
  405.             if (particlePtr->lifeRemaining > 0)
  406.             {
  407.                 horizLoc = SW_FIX2INT(particlePtr->oldHorizLoc);
  408.                 vertLoc = SW_FIX2INT(particlePtr->oldVertLoc);
  409.                 
  410.                     // First erase the particle from its old position
  411.                 if ((horizLoc < visBoundsRight) && (horizLoc > visBoundsLeft) &&
  412.                     (vertLoc < visBoundsBottom) && (vertLoc > visBoundsTop))
  413.                 {
  414.                     SET_DEPTH_PIXEL( windowFrameP, horizLoc, vertLoc ,
  415.                         GET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ) )
  416.                 }
  417.                 
  418.                 horizLoc = SW_FIX2INT(particlePtr->horizLoc);
  419.                 vertLoc = SW_FIX2INT(particlePtr->vertLoc);
  420.  
  421.                     // Now draw the particle in its new position
  422.                 if ((horizLoc < visBoundsRight) && (horizLoc > visBoundsLeft) &&
  423.                     (vertLoc < visBoundsBottom) && (vertLoc > visBoundsTop))
  424.                 {
  425.                     SET_DEPTH_PIXEL( windowFrameP, horizLoc, vertLoc ,
  426.                         GET_DEPTH_PIXEL( workFrameP, horizLoc, vertLoc ) )
  427.                 }
  428.             }
  429.         }
  430.     )
  431.     
  432.     END_32_BIT_MODE
  433.     
  434.         // Move them *after* drawing them!
  435.     MoveParticles();
  436. }
  437.  
  438.  
  439. ///--------------------------------------------------------------------------------------
  440. //  UpdateParticlesInScrollingWindow - moves particles, but does not need to draw them,
  441. //    since the entire offscreen is copied to the screen in a scrolling animation.
  442. ///--------------------------------------------------------------------------------------
  443.  
  444. SW_FUNC void    UpdateParticlesInScrollingWindow( SpriteWorldPtr spriteWorldP )
  445. {
  446.     #pragma unused(spriteWorldP)
  447.     SW_ASSERT(gParticlesInitialized == true);
  448.  
  449.     // we do not need to draw them here, since the entire offscreen is copied to the screen
  450.     
  451.     MoveParticles();
  452. }
  453.  
  454.  
  455. ///--------------------------------------------------------------------------------------
  456. //  MoveParticles - called internally by UpdateParticlesInScrollingWindow and
  457. //    UpdateParticlesInWindow.
  458. ///--------------------------------------------------------------------------------------
  459.  
  460. SW_FUNC void    MoveParticles( void )
  461. {
  462.     Particle        *particlePtr;
  463.     long            index,  count = gLastElementInUse;
  464.     
  465.     if (gParticlesInitialized == false)
  466.         return;
  467.     
  468.     for (index = 0, particlePtr = gParticleArray; index <= count; index++, particlePtr++)
  469.     {
  470.         SW_ASSERT(index < gNumArrayElements);
  471.         
  472.         if (particlePtr->lifeRemaining > 0)
  473.         {
  474.             particlePtr->lifeRemaining--;
  475.             
  476.             if (particlePtr->lifeRemaining == 0)
  477.             {
  478.                 gNumberOfParticlesActive--;
  479.                 
  480.                     // If this dead particle opened up a space, update gLowestUnusedElement.
  481.                 if (index < gLowestUnusedElement)
  482.                     gLowestUnusedElement = index;
  483.             }
  484.             else
  485.             {
  486.                 particlePtr->oldHorizLoc = particlePtr->horizLoc;
  487.                 particlePtr->oldVertLoc = particlePtr->vertLoc;
  488.  
  489.                 particlePtr->horizLoc += particlePtr->horizSpeed;
  490.                 particlePtr->vertLoc += particlePtr->vertSpeed;
  491.  
  492.                 particlePtr->vertSpeed += gGravity;
  493.             }
  494.         }
  495.     }
  496.     
  497.     if (gNumberOfParticlesActive <= 0)
  498.     {
  499.         gLastElementInUse = 0;
  500.     }
  501.     else
  502.     {
  503.             // See if the gLastElementInUse isn't in use any more.
  504.             // If not, find the next element that still is.
  505.         while (gParticleArray[gLastElementInUse].lifeRemaining == 0 && gLastElementInUse > 0)
  506.             gLastElementInUse--;
  507.     }
  508. }